/**************************************************************************
 * @name: rwlock.c
 * @brief: 读写锁代码练习一
 * @date: 2025/02/14
 * @author: boluzs@163.com
 * @note: None
 * @version: 1.0.0
 * @Copyright (c) 2025 by boluzs@163.com, All Rights Reserved.
 **************************************************************************/
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <time.h>
#include <stdlib.h>

#include <time.h>
#include <pthread.h>
#include <unistd.h>
#include <semaphore.h>
#define FILE_PATH "./log.txt"
// 假设创建子线程用于获取当前系统时间并转化为固定格式的字符串，存储到缓冲区，主线程从缓冲区中读取字符串

// 临界资源，访问的应该互斥访问
char timebuf[128] = {0}; // 用于记录转化好的系统时间  "2025/2/13 hh:mm:ss"

// 定义读写锁，用于多线程间的互斥访问，该读写锁的属性是默认的，已经初始化过的
pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER;

// 定义POSIX匿名信号量，只能在进程中的线程间使用，一般是用于多线程的同步
sem_t sem;

// 构造线程的回调函数 格式是固定的
void *threadA_handler(void *arg)
{
    char date[128] = {0};
    for (;;)
    {
        // P操作
        sem_wait(&sem);

        // 上锁操作
        pthread_rwlock_rdlock(&rwlock);

        FILE *fp = fopen(FILE_PATH, "rb");
        if (NULL == fp)
        {
            fprintf(stderr, "fopen [%s] error,%s\n", FILE_PATH, strerror(errno));
            exit(-1);
        }

        fgets(date, 128, fp);

        fclose(fp);

        // 解锁操作
        pthread_rwlock_unlock(&rwlock);

        char *pstr = strtok(date, ","); // 返回的地址指向的是逗号前面的字符串
        if (NULL == pstr)
        {
            fprintf(stderr, "strtok error,%s\n", strerror(errno));
            exit(-2);
        }

        printf("%s\n", pstr);

        // V操作
        sem_post(&sem);
        sleep(1);
    }
}

// 构造线程的回调函数 格式是固定的
void *threadB_handler(void *arg)
{
    char date[128] = {0};
    for (;;)
    {
        // P操作
        sem_wait(&sem);
        // 上锁操作
        pthread_rwlock_rdlock(&rwlock);

        FILE *fp = fopen(FILE_PATH, "rb");
        if (NULL == fp)
        {
            fprintf(stderr, "fopen [%s] error,%s\n", FILE_PATH, strerror(errno));
            exit(-1);
        }

        fgets(date, 128, fp);

        fclose(fp);

        // 解锁操作
        pthread_rwlock_unlock(&rwlock);

        char *pstr = strtok(date, ","); // 返回的地址指向的是逗号前面的字符串
        if (NULL == pstr)
        {
            fprintf(stderr, "strtok error,%s\n", strerror(errno));
            exit(-2);
        }
        pstr = strtok(NULL, ","); // 返回的地址指向的是逗号后面的字符串

        printf("%s\n", pstr);

        // V操作
        sem_post(&sem);
        sleep(1);
    }
}

int main()
{

    pthread_t newthreadA_id = 0; // 用于记录创建的新线程的ID，可以理解为是线程的句柄
    pthread_t newthreadB_id = 0; // 用于记录创建的新线程的ID，可以理解为是线程的句柄

    // 0.初始化POSIX匿名信号量,并且设置信号量初值为0
    int ret = sem_init(&sem, 0, 1);
    if (-1 == ret)
    {
        fprintf(stderr, "sem_init error,%s\n", strerror(errno));
        exit(-1);
    }

    // 1.创建新线程
    pthread_create(&newthreadA_id, NULL, threadA_handler, NULL);
    pthread_create(&newthreadB_id, NULL, threadB_handler, NULL);

    // 2.创建新文本，如果文本不存在则创建
    FILE *fp = fopen(FILE_PATH, "ab");
    if (NULL == fp)
    {
        fprintf(stderr, "fopen [%s] error,%s\n", FILE_PATH, strerror(errno));
        exit(-1);
    }

    // 主线程的任务
    for (;;)
    {

        // 上锁操作
        pthread_rwlock_wrlock(&rwlock);

        // 获取系统时间
        time_t ptime = time(NULL); // 以秒为单位

        // 转化系统时间
        struct tm *ptm = localtime(&ptime);
        fprintf(fp, "date:%d/%d/%d,time:%02d:%02d:%02d\n",
                ptm->tm_year + 1900,
                ptm->tm_mon + 1,
                ptm->tm_mday,
                ptm->tm_hour,
                ptm->tm_min,
                ptm->tm_sec

        );
        // 解锁操作
        pthread_rwlock_unlock(&rwlock);

        // V操作
        sem_post(&sem);
        sleep(5);
    }

    fclose(fp);
    return 0;
}
